/** @file

Copyright (c) 2006-2023, Kunlun BIOS, Kunlun Technology (Beijing) Co., Ltd.. All
Rights Reserved.

You may not reproduce, distribute, publish, display, perform, modify, adapt,
transmit, broadcast, present, recite, release, license or otherwise exploit
any part of this publication in any form, by any means, without the prior
written permission of Kunlun Technology (Beijing) Co., Ltd..

Module Name:


Abstract:


Revision History:

**/


#include <SetupHiiData.h>
#include <SetupConfig.h>
//[gliu-0005]
#include <Library/KlSetupVarRWLib.h>
#include <Library/SetupSaveDefaultHookLib.h> //[Libo-0001]
#include <OemConfigData.h>
//[gliu-0005]

BOOLEAN   mHiiDataRegistered = FALSE;

#pragma pack(1)
typedef struct {
  VENDOR_DEVICE_PATH             VendorDevicePath;
  UINT32                         Reserved;
  UINT64                         UniqueId;
} HII_VENDOR_DEVICE_PATH_NODE;
#pragma pack()

typedef struct {
  HII_VENDOR_DEVICE_PATH_NODE    Node;
  EFI_DEVICE_PATH_PROTOCOL       End;
} HII_TEMP_DEVICE_PATH;



HII_TEMP_DEVICE_PATH  mHiiVendorDevicePathTemplate = {
  {
    {
      {
        HARDWARE_DEVICE_PATH,
        HW_VENDOR_DP,
        {
          (UINT8) (sizeof (HII_VENDOR_DEVICE_PATH_NODE)),
          (UINT8) ((sizeof (HII_VENDOR_DEVICE_PATH_NODE)) >> 8)
        }
      },
      EFI_IFR_TIANO_GUID,
    },
    0,
    0
  },
  {
    END_DEVICE_PATH_TYPE,
    END_ENTIRE_DEVICE_PATH_SUBTYPE,
    {
      END_DEVICE_PATH_LENGTH,
      0
    }
  }
};
NEW_PACKAGE_INFO               mNewPackageInfo [] =
                                 {
                                   {InstallExitCallbackRoutine,     ExitVfrBin,      SetupLibStrings,           NULL,   FORMSET_ID_GUID_EXIT},
                                   {InstallBootCallbackRoutine,     BootVfrBin,      SetupHiiDataStrings,       NULL,   FORMSET_ID_GUID_BOOT},
                                   {InstallSecurityCallbackRoutine, SecurityVfrBin,  SetupHiiDataStrings,       NULL,   FORMSET_ID_GUID_SECURITY},
                                   //[jiangfengzhang-0004-begin]
                                   {InstallPowerManageCallbackRoutine, PowerManageVfrBin,  SetupLibStrings,     NULL,   FORMSET_ID_GUID_POWER_MANAGE},
                                   //[jiangfengzhang-0004-end]
                                   {InstallAdvanceCallbackRoutine,  AdvanceVfrBin,   SetupLibStrings,           NULL,   FORMSET_ID_GUID_ADVANCE},
                                   {InstallMainCallbackRoutine,     MainVfrBin,      SetupLibStrings,           NULL,   FORMSET_ID_GUID_MAIN},
                                 };

SETUP_HII_DATA_MANAGMENT_PROTOCOL                   *gSetupHiiDataMgr;
EFI_GUID  mFormSetGuid    = SYSTEM_CONFIGURATION_GUID;
CHAR16    mVariableName[] = L"SystemSetupConfig";
SETUP_HII_PRIVATE_DATA                   *gHiiData;

//add-klk-lyang-P000A-start//
extern EFI_STATUS
InstallDeviceInfoHiiData (
  BOOLEAN              SetToDefaults
);

extern EFI_STATUS
DestroyDeviceInfoData (
  VOID
  );
//add-klk-lyang-P000A-end//

VOID
SetupAppNotifyFn (
  IN EFI_EVENT                             Event,
  IN VOID                                  *Context
  );

EFI_STATUS
CreateScuData (
  VOID
  );

EFI_STATUS
DestroyScuData (
  VOID
  );

/**
  Brief description of InitHiiPrivateData.

  @retval EFI_SUCCESS   Function successful returned.
**/
EFI_STATUS
InitHiiPrivateData (
  VOID
)
{
  EFI_STATUS                                Status;
  UINTN                                     BufferSize;

  gHiiData = AllocateZeroPool (sizeof(SETUP_HII_PRIVATE_DATA));
  if (gHiiData == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  BufferSize = sizeof (SYSTEM_SETUP_CONFIGURATION);
  Status = gRT->GetVariable (SETUP_VARIABLE_NAME, &gSystemConfigurationGuid, NULL, &BufferSize, &(gHiiData->Configuration));

  Status = gBS->LocateProtocol (
                  &gEfiHiiDatabaseProtocolGuid,
                  NULL,
                  (VOID **)&gHiiData->HiiDatabase
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // There should only be one HII protocol
  //
  Status = gBS->LocateProtocol (
                  &gEfiHiiConfigRoutingProtocolGuid,
                  NULL,
                  (VOID **)&gHiiData->HiiConfigRouting
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  return EFI_SUCCESS;
};

/**
  Notification function of EFI_END_OF_DXE_EVENT_GROUP_GUID event group.

  This is a notification function registered on EFI_END_OF_DXE_EVENT_GROUP_GUID event group.

  @param  Event        Event whose notification function is being invoked.
  @param  Context      Pointer to the notification function's context.

**/
VOID
EFIAPI
ResetSystemEndOfDxeEventNotify (
  EFI_EVENT                               Event,
  VOID                                    *Context
  )
{
  gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL);
}



EFI_STATUS
InstallSetupHiiDataProtocol (
  IN  VOID
  );
/**
The driver add setup formset package to hii database.
**/
EFI_STATUS
SetupHiiDataInit (
  IN EFI_HANDLE                         ImageHandle,
  IN EFI_SYSTEM_TABLE                   *SystemTable
  )
{

  EFI_STATUS                            Status;
  // EFI_EVENT                             Event;
//add-klk-lyang-P000A-start//
  //EFI_EVENT                             EndOfDxeEvent;
//add-klk-lyang-P000A-end//
  //[gliu-0002]
  EFI_EVENT                             EndOfDxeEvent;
  //[gliu-0002]
  VOID                                  *Registration;
  SYSTEM_SETUP_CONFIGURATION            *Configuration;
  UINTN                                 BufferSize;
  SYSTEM_ACCESS                         SystemAccess;
  OLD_PASSWORD_DATA                     OldPassword;
  EFI_GUID                              SysAccessGuid = SYSTEM_ACCESS_GUID;
  KUNLUN_HIDE_HOEKEY                    HideHotkey;


  //
  //Init Local variable
  //
  Configuration = NULL;
  BufferSize = 0;

  EfiCreateProtocolNotifyEvent (
            &gEfiSetupUtilityApplicationProtocolGuid,
            TPL_NOTIFY,
            SetupAppNotifyFn,
            NULL,
            &Registration
            );

  Status = InitHiiPrivateData();
  InstallSetupHiiDataProtocol();

  BufferSize = sizeof (OLD_PASSWORD_DATA);
  Status = gRT->GetVariable (L"OldPassword", &SysAccessGuid, NULL, &BufferSize, &OldPassword);
  if (EFI_ERROR(Status)) {
    ZeroMem(&OldPassword, BufferSize);
    Status = gRT->SetVariable(
                  L"OldPassword",
                  &SysAccessGuid,
                  EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
                  sizeof (OLD_PASSWORD_DATA),
                  &OldPassword
                  );
  }

//add-klk-lyang-P000A-start//
  BufferSize = sizeof (SYSTEM_ACCESS);
  Status = gRT->GetVariable (L"SystemAccess", &SysAccessGuid, NULL, &BufferSize, &SystemAccess);
  if (EFI_ERROR (Status)) {
    Status = gRT->GetVariable (L"SystemAccess", &SysAccessGuid, NULL, &BufferSize, &SystemAccess);
    if (EFI_ERROR (Status)) {
      ZeroMem(&SystemAccess, BufferSize);
      Status = gRT->SetVariable(
              L"SystemAccess",
              &SysAccessGuid,
              EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
              sizeof (SYSTEM_ACCESS),
              &SystemAccess
              );
    }
  BufferSize = sizeof (KUNLUN_HIDE_HOEKEY);
  Status = gRT->GetVariable (L"KunlunHideHotkey", &gKunlunHideHotkeyGuid, NULL, &BufferSize, &HideHotkey);
  if (EFI_ERROR (Status)) {
    HideHotkey.HideSwitch = 0x5a;
    Status = gRT->SetVariable(
                    L"KunlunHideHotkey",
                    &gKunlunHideHotkeyGuid,
                    EFI_VARIABLE_BOOTSERVICE_ACCESS,
                    sizeof (KUNLUN_HIDE_HOEKEY),
                    &HideHotkey
                    );
    }
  }
//add-klk-lyang-P000A-end//
  //
  //need add hii packages and set variable to default when set configuration variable not exist.
  //
  Configuration = &gHiiData->Configuration;
  BufferSize = sizeof (SYSTEM_SETUP_CONFIGURATION);
  ZeroMem (Configuration, sizeof (SYSTEM_SETUP_CONFIGURATION));
  Status = gRT->GetVariable (SETUP_VARIABLE_NAME, &gSystemConfigurationGuid, NULL, &BufferSize, Configuration);
  if (EFI_ERROR (Status)) {
    CreateScuData();
    //if (!GetCmosFlag()){
      //[jdzhang-0004]
      //Status = gBS->CreateEventEx (
      //               EVT_NOTIFY_SIGNAL,
      //               TPL_CALLBACK,
      //               ResetSystemEndOfDxeEventNotify,
      //               NULL,
      //               &gEfiEndOfDxeEventGroupGuid,
      //               &EndOfDxeEvent
      //               );
      //[jdzhang-0004]
      //[gliu-0002]
      Status = gBS->CreateEventEx (
                     EVT_NOTIFY_SIGNAL,
                     TPL_CALLBACK,
                     ResetSystemEndOfDxeEventNotify,
                     NULL,
                     &gEfiEndOfDxeEventGroupGuid,
                     &EndOfDxeEvent
                     );
      //[gliu-0002]
    //}
  }

  return EFI_SUCCESS;
}

EFI_STATUS
CreateHiiDriverHandle (
  OUT EFI_HANDLE               *DriverHandle
  )
{
  EFI_STATUS                   Status;
  HII_VENDOR_DEVICE_PATH_NODE  *VendorDevicePath;

  VendorDevicePath = AllocateCopyPool (sizeof (HII_TEMP_DEVICE_PATH), &mHiiVendorDevicePathTemplate);
  if (VendorDevicePath == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Use memory address as unique ID to distinguish from different device paths
  //
  VendorDevicePath->UniqueId = (UINT64) ((UINTN) VendorDevicePath);

  *DriverHandle = NULL;
  Status = gBS->InstallMultipleProtocolInterfaces (
                  DriverHandle,
                  &gEfiDevicePathProtocolGuid,
                  VendorDevicePath,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  return EFI_SUCCESS;
}

//[gliu-0005]
STATIC
VOID
InitEcVar()
{
 SYSTEM_SETUP_CONFIGURATION        *SetupVar = NULL;
  UINTN  VarSize = sizeof(OEM_CONFIG_DATA);
  OEM_CONFIG_DATA  OemConfigData;
  EFI_STATUS                   Status;

  Status = KlSetupVarRead(&SetupVar);
  if(!EFI_ERROR(Status))
  {

     Status = gRT->GetVariable (
                        OEM_CONFIG_NAME,
                        &gOemDataSetupGuid,
                        NULL,
                        &VarSize,
                        &OemConfigData
                        );

    if(!EFI_ERROR(Status))
    {
      SetupVar->IsEcExisted = OemConfigData.IsEcExisted;
      DEBUG((EFI_D_ERROR, "Set var IsEcExisted:%d\n",SetupVar->IsEcExisted));
    }else{
      SetupVar->IsEcExisted = 0;
    }
    KlSetupVarWrite(SetupVar);
    FreePool(SetupVar);
  }
DEBUG((EFI_D_ERROR, "Get var IsEcExisted Status:%r\n",Status));
}
//[gliu-0005]

EFI_STATUS
InstallHiiData (
  VOID
  )
{
  EFI_HII_HANDLE                       HiiHandle = NULL;
  EFI_HANDLE                           DriverHandle;
  UINTN                                Index;
  EFI_STATUS                           Status;
  UINTN                                HandleCnt;
  UINTN                                BufferSize;
  SYSTEM_SETUP_CONFIGURATION           *Configuration;
  EFI_STRING                           ConfigRequestHdr;
  BOOLEAN                              ActionFlag;
  EFI_STRING_ID                        TempToken;
  EFI_IFR_FORM_SET                     *FormSetPtr;
  UINT8                                *TempPtr;
  BOOLEAN                              DefaultToSetup;

  HandleCnt = sizeof (mNewPackageInfo) / sizeof (NEW_PACKAGE_INFO);
  DefaultToSetup = FALSE;

  gSetupHiiDataMgr->MenuItemCount = 0;
  if (HandleCnt > MAX_HII_HANDLES) {
    return EFI_OUT_OF_RESOURCES;
  }

  Status = EFI_SUCCESS;
  BufferSize = sizeof (SYSTEM_SETUP_CONFIGURATION);
  Configuration = &gHiiData->Configuration;
  //DEBUG ((EFI_D_ERROR, "HandleCnt %02x", HandleCnt));

  //
  //Process configuration
  //
  Configuration = &gHiiData->Configuration;

  //
  // Try to read NV config EFI variable first
  //

    //ASSERT (ConfigRequestHdr != NULL);

  BufferSize = sizeof (SYSTEM_SETUP_CONFIGURATION);
  Status = gRT->GetVariable (SETUP_VARIABLE_NAME, &gSystemConfigurationGuid, NULL, &BufferSize, Configuration);
  if (EFI_ERROR(Status)) {
    ZeroMem (Configuration, sizeof (SYSTEM_SETUP_CONFIGURATION));
    //
    // Store zero data Buffer Storage to EFI variable
    //
    Status = gRT->SetVariable(
                    SETUP_VARIABLE_NAME,
                    &gSystemConfigurationGuid,
                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
                    sizeof (SYSTEM_SETUP_CONFIGURATION),
                    Configuration
                    );
    DefaultToSetup = TRUE;
  }

//add-klk-lyang-P000A-start//
  //InstallDeviceInfoHiiData(DefaultToSetup);
//add-klk-lyang-P000A-end//
//[gliu-0005]
  InitEcVar();
//[gliu-0005]

  for (Index = 0; Index < HandleCnt; Index++) {
    Status = CreateHiiDriverHandle (&DriverHandle);
    if (EFI_ERROR (Status)) {
      break;
    }

    HiiHandle = HiiAddPackages (
                  &mFormSetGuid,
                  DriverHandle,
                  mNewPackageInfo[Index].IfrPack,
                  mNewPackageInfo[Index].StringPack,
                  NULL
                  );

    if (HiiHandle == NULL) {
      gST->ConOut->ClearScreen (gST->ConOut);
      gST->ConOut->OutputString (gST->ConOut,L"add hii package fail\n");  //Just for debug

    }

    //
    // Skips the header, now points to data
    //

    TempPtr = mNewPackageInfo[Index].IfrPack;

    TempPtr = TempPtr + sizeof(UINT32);

    TempPtr = (UINT8 *) ((EFI_HII_FORM_PACKAGE_HDR *) TempPtr + 1);

    FormSetPtr = (EFI_IFR_FORM_SET *) TempPtr;

    mNewPackageInfo[Index].CallbackRoutine (DriverHandle, HiiHandle);
    if (gSetupHiiDataMgr!=NULL) {
      //
      // Set the display page.  Last page found is the first to be displayed.
      //
      (gSetupHiiDataMgr->MenuList[Index]).Page = HiiHandle;
      TempToken = FormSetPtr->FormSetTitle;
      gSetupHiiDataMgr->MenuList[Index].MenuTitle = TempToken;
      //
      // NULL out the string pointer
      //
      gSetupHiiDataMgr->MenuList[Index].String = NULL;
      gSetupHiiDataMgr->MenuList[Index].String = SetupGetToken(gSetupHiiDataMgr->MenuList[Index].MenuTitle,
                                                  (EFI_HII_HANDLE) gSetupHiiDataMgr->MenuList[Index].Page
                                                 );
      gSetupHiiDataMgr->MenuList[Index].FormSetGuid = &mNewPackageInfo[Index].FormSetGuid;
      //DEBUG ((EFI_D_ERROR, "TANG Menulist %s\n",gSetupHiiDataMgr->MenuList[Index].String));
      gSetupHiiDataMgr->MenuItemCount++;
    }
    ConfigRequestHdr = HiiConstructConfigHdr (&gSystemConfigurationGuid, SETUP_VARIABLE_NAME, DriverHandle);
    ASSERT(ConfigRequestHdr != NULL);
    if (DefaultToSetup) {
      ActionFlag = HiiSetToDefaults (ConfigRequestHdr, EFI_HII_DEFAULT_CLASS_STANDARD);
      if (!ActionFlag) {
        FreePool (ConfigRequestHdr);
        continue;
      }
    } else {
      //
      // EFI variable does exist and Validate Current Setting
      //
      ActionFlag = HiiValidateSettings (ConfigRequestHdr);
      if (!ActionFlag) {
        FreePool (ConfigRequestHdr);
        continue;
      }
    }

    FreePool (ConfigRequestHdr);
  }
//start-klk-lyang-P000A-remove//
  //UpdateSetupPageStrings(HiiHandle);

  Status = gRT->GetVariable (SETUP_VARIABLE_NAME, &gSystemConfigurationGuid, NULL, &BufferSize, Configuration);
  if (!EFI_ERROR(Status)) {
    gSetupHiiDataMgr->SCBuffer = (UINT8*)Configuration;
  }
  gSetupHiiDataMgr->Size = sizeof (SYSTEM_SETUP_CONFIGURATION);
//end-klk-lyang-P000A-end//
  return EFI_SUCCESS;
}

VOID
SetupAppNotifyFn (
  IN EFI_EVENT                             Event,
  IN VOID                                  *Context
  )
{
  EFI_STATUS                               Status;
  EFI_SETUP_UTILITY_APPLICATION_PROTOCOL   *SetupUtilityApp;

  Status = gBS->LocateProtocol (
                  &gEfiSetupUtilityApplicationProtocolGuid,
                  NULL,
                  (VOID **) &SetupUtilityApp
                  );
  if (EFI_ERROR(Status)) {
    return;
  }
  switch (SetupUtilityApp->VfrDriverState) {

  case InitializeSetupUtility:
    SetupSetConsoleMode();
    CreateScuData ();
    break;

  case ShutdownSetupUtility:
    DestroyScuData ();
    break;

  default:
    break;
  }

  return;
}

EFI_STATUS
CreateScuData (
  VOID
  )
{
  EFI_STATUS  Status = EFI_SUCCESS;
  if (!mHiiDataRegistered) {
    mHiiDataRegistered = TRUE;
    Status = InstallHiiData ();
  }
  return   Status;
}

EFI_STATUS
RemoveHiiData (
)
{
  EFI_STATUS                                Status;
  EFI_HII_DATABASE_PROTOCOL                 *HiiDatabase;
  UINTN                                     Index;

  Status = gBS->LocateProtocol (
                  &gEfiHiiDatabaseProtocolGuid,
                  NULL,
                  (VOID **)&HiiDatabase
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Index = 0;
  for (Index = 0;Index< gSetupHiiDataMgr->MenuItemCount;Index++) {
    HiiDatabase->RemovePackageList (HiiDatabase, (gSetupHiiDataMgr->MenuList[Index]).Page);
  }
  return EFI_SUCCESS;
}

EFI_STATUS
DestroyScuData (
  VOID
  )
{
  EFI_STATUS         Status;
//add-klk-lyang-P000A-start//
  //Status = DestroyDeviceInfoData();
//add-klk-lyang-P000A-end//
  Status = RemoveHiiData();
//start-klk-lyang-P000A-add//
  if (!EFI_ERROR (Status)) {
    mHiiDataRegistered = FALSE;
  }
//end-klk-lyang-P000A-end//
  return Status;
}

EFI_STATUS
InstallSetupHiiDataProtocol (
  IN  VOID
  )
{
  EFI_STATUS                                 Status;
  EFI_HANDLE                                 Handle = NULL;

  gSetupHiiDataMgr = AllocateZeroPool (sizeof (SETUP_HII_DATA_MANAGMENT_PROTOCOL));
  if (gSetupHiiDataMgr == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  gSetupHiiDataMgr->SUCInfo = AllocateZeroPool (sizeof(SETUP_UTILITY_CONFIGURATION));
//add-klk-lyang-P000A-start//
  gSetupHiiDataMgr->SCBuffer = NULL;
//add-klk-lyang-P000A-end//
  gSetupHiiDataMgr->ExtensionSignature    = SETUP_DATA_MANAGER_EXTENSION_SIGNATURE;
  gSetupHiiDataMgr->ExtensionVersion           = 0x6;
  gSetupHiiDataMgr->SetupDefaultCallBack       = SetupDefaultCallBack;
  gSetupHiiDataMgr->SetupSubmitCallBack        = SetupSubmitCallBack;
  gSetupHiiDataMgr->SetupDefaultAuth           = SetupDefaultAuth;
  gSetupHiiDataMgr->SetupSubmitAuth            = SetupSubmitAuth;
  gSetupHiiDataMgr->PopUpHistory               = FixedPcdGetBool(PcdSetupPopUpHistory);
//add-klk-lyang-virtkey-001-add//
  gSetupHiiDataMgr->SetupMouseVirkKey          = FixedPcdGetBool(PcdSetupMouseVirkKeyDefault);
//add-klk-lyang-setup-004-add//
  gSetupHiiDataMgr->SetupBootOptionXkeyEnable  = FixedPcdGetBool(PcdSetupBootOptionXkeyEnable);
//add-klk-lyang-setup-004-end//
//[-start-klk-lyang-010-20221202-add]//
  gSetupHiiDataMgr->RefreshSetupKeyPressEnable = FixedPcdGetBool(PcdRefreshSetupKeyPressEnable);
  gSetupHiiDataMgr->HideHotKeyPressEnable      = FixedPcdGetBool(PcdHideHotKeyPressEnable);
//[-end-klk-lyang-010-20221202-add]//
//[-start-klk-lyang-014-20221202-add]//
  gSetupHiiDataMgr->GrayoutAllUserOpcode       = FixedPcdGetBool(PcdGrayoutAllUserOpcodeEnable);
  Status = gBS->InstallProtocolInterface (
                  &Handle,
                  &gSetupHiiDataManagmentProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  gSetupHiiDataMgr
                  );

  return Status;
}
